package io.renren.modules.iots.utils.socket.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.alibaba.fastjson.JSON;

import io.renren.modules.iots.entity.CollectionAgentConfig;
import io.renren.modules.iots.entity.IOTPBean;
import io.renren.modules.iots.utils.comment.IotpBodyUtils;
import io.renren.modules.iots.utils.comment.Msg_rellect;
import io.renren.modules.iots.utils.socket.client.SingleSocketWriteThread;  

/**
 * @ClassName: NIOSServerForControl
 * @Description: 专门用于和控制借口服务通讯使用
 * @author 周西栋
 * @date 2018年6月28日
 */
public class NIOSServerForControl {
	
	/************************************************      单例模式     ************************************************/
	
    /**
     * 创建一个新的实例 NIOSServerForControl.
     */
    private NIOSServerForControl(){
        try {
            init();
            listen();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 私有对象变量
     */
    private static final NIOSServerForControl single = new NIOSServerForControl();
    
    /**
     * @Title: getInstance
     * @Description: 饿汉模式--单例（静态工厂方法） 
     * @param @return    参数
     * @return NIOSServerForControl    返回类型
     * @throws
     */
    public static NIOSServerForControl getInstance() {
        return single;
    }
	
	/************************************************      单例模式     ************************************************/
	
	/**
	 * 协议解析工具类
	 */
	private static IotpBodyUtils ibu = new IotpBodyUtils();
	
	/**
	 * server的端口号
	 */
    private int port = 8889;
    
    /*接受数据缓冲区*/
    private ByteBuffer rBuffer = ByteBuffer.allocate(8354);
    
    /*映射客户端channel */
    private Selector selector;
    
    /**
     * 数据拼接工具类
     */
    private Msg_rellect mr = new Msg_rellect();
    
    /**
     * 容器list
     */
    private List<byte[]> byte_list = new ArrayList<>();
    
    /**
     * 需要写回去的信息
     */
    private List<byte[]> send_list = new ArrayList<>();
    
    /**
     * 拿到写线程
     */
    SingleSocketWriteThread write = SingleSocketWriteThread.getInstance();
    
    private void init() throws IOException{
        /* 
         *启动服务器端，配置为非阻塞，绑定端口，注册accept事件 
         *ACCEPT事件：当服务端收到客户端连接请求时，触发该事件 
         */  
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  
        serverSocketChannel.configureBlocking(false);  
        ServerSocket serverSocket = serverSocketChannel.socket();  
        serverSocket.bind(new InetSocketAddress(port));  
        selector = Selector.open();  
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);  
        System.out.println("server start on port:"+port);  
    }

    /** 
     * 服务器端轮询监听，select方法会一直阻塞直到有相关事件发生或超时 
     */  
    private void listen(){

        while (true) {
            try {
                selector.select();//返回值为本次触发的事件数  
                Set<SelectionKey> selectionKeys = selector.selectedKeys();  
                for(SelectionKey key : selectionKeys){
                    handle(key);
                    Thread.sleep(20);
                }
                selectionKeys.clear();//清除处理过的事件 
            } catch (Exception e) {
            	// 不做任何处理
            }  
        }
    }

    /**
     * 处理不同的事件 
     */ 
    private void handle(SelectionKey selectionKey) throws IOException { 

        ServerSocketChannel server = null;
        SocketChannel client = null;
        int count=0;
        if (selectionKey.isAcceptable()) {
        	/* 
             * 客户端请求连接事件 
             * 拿到所有的注册标记,将其他的注册标记删除
             */
        	Set<SelectionKey> selectionKeys = selector.selectedKeys();  
        	for(SelectionKey key : selectionKeys){
        		if(key != selectionKey) {
        			// 关闭原来的通道
        			key.channel().close();
        			// 注销原来的标记
        			key.cancel();
        		}
        	}
        	// 初始化工具
        	initTools();
            /* 
             * 客户端请求连接事件 
             * serversocket为该客户端建立socket连接，将此socket注册READ事件，监听客户端输入 
             * READ事件：当客户端发来数据，并已被服务器控制线程正确读取时，触发该事件 
             */  
            server = (ServerSocketChannel) selectionKey.channel();
            client = server.accept();
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ);
        } else if (selectionKey.isReadable()) {
            /*
             * READ事件，收到客户端发送数据，读取数据后继续注册监听客户端 
             */
            client = (SocketChannel) selectionKey.channel();
            try {
				count = client.read(rBuffer);
				if (count > 0) {
					rBuffer.flip();
					ibu = new IotpBodyUtils();
					byte[] data = ibu.getNewByteArray(rBuffer.array(), 0, count);
	                byte_list = mr.rellect(data); 
	                if(byte_list != null && byte_list.size() > 0){
	                	for(byte[] b : byte_list){
	                		IOTPBean iotp = ibu.toIotpBean(b);
	                		if(iotp != null && iotp.getBody() != null){
	                			// 交给业务处理
	                			hander(iotp);
	                			// 将信息写回客户端
	                			if(send_list.size() > 0) {
	                				send_buffer(client,send_list.get(0));
	                			}
	                		}
	                	}
	                }
	            }  
            } catch (Exception e1) {
				e1.printStackTrace();
			} finally {
				rBuffer.clear();
			}
        }   
    }  
    
    /**
     * @author 周西栋
     * @date 2018年6月28日
     * @version V1.0
     * @Title: send_buffer
     * @Description: 想客户端发送数据
     * @param @param socket
     * @param @param msg
     * @param @throws IOException
     * @param @throws InterruptedException    参数
     * @return void    返回类型
     * @throws
     */
    private void send_buffer(SocketChannel socket,byte[] msg) throws IOException, InterruptedException {
    	int p = 0;
		while(p < msg.length){
			int sendSize = socket.write(ByteBuffer.wrap(msg,p,msg.length-p));
			p += sendSize;
			if(p != msg.length && sendSize > 0){
				Thread.sleep(1);
			}
		}
    }
    
	/**
	 * @author 周西栋
	 * @date 2018年6月28日
	 * @version V1.0
	 * @Title: initTools
	 * @Description: 初始化解析工具
	 * @param     参数
	 * @return void    返回类型
	 * @throws
	 */
	private void initTools() {
		this.rBuffer.clear();
		this.mr = null;
		this.mr = new Msg_rellect(); 
		this.byte_list.clear();
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月28日
	 * @version V1.0
	 * @Title: send_Msg
	 * @Description: 发送一组信息
	 * 				当selector里面已经不存在注册信息时，不让在发送
	 * 				当send_list里面存入了50万条数据时，不让在发送
	 * @param @param send_list
	 * @param @return    参数
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_Msg(List<byte[]> send_list) {
		if(!checkSendMsg()) {
			return false;
		}else {
			this.send_list.addAll(send_list);
			return true;
		}
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月28日
	 * @version V1.0
	 * @Title: send_Msg
	 * @Description: 发送单条信息
	 * 				当selector里面已经不存在注册信息时，不让在发送
	 * 				当send_list里面存入了50万条数据时，不让在发送
	 * @param @param send_msg
	 * @param @return    参数
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_Msg(byte[] send_msg) {
		if(!checkSendMsg()) {
			return false;
		}else {
			this.send_list.add(send_msg);
			return true;
		}
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月28日
	 * @version V1.0
	 * @Title: send_Msg
	 * @Description: 发送单条iotp信息
	 * 				当selector里面已经不存在注册信息时，不让在发送
	 * 				当send_list里面存入了50万条数据时，不让在发送
	 * @param @param iotp
	 * @param @return    参数
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_Msg(IOTPBean iotp) {
		if(!checkSendMsg()) {
			return false;
		}else {
			this.send_list.addAll(ibu.toByteArray(iotp));
			return true;
		}
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月28日
	 * @version V1.0
	 * @Title: checkSendMsg
	 * @Description: 检查是否满足发送信息的条件
	 * 				当selector里面已经不存在注册信息时，不让在发送
	 * 				当send_list里面存入了50万条数据时，不让在发送
	 * @param @return    参数
	 * @return boolean    返回类型
	 * @throws
	 */
	private boolean checkSendMsg() {
		if(send_list == null || send_list.size() == 0 || selector.selectedKeys().size() == 0 || send_list.size() > 500000) {
			return false;
		}
		return true;
	}
	
	
	/**
	 * @throws InterruptedException 
	 * @author 周西栋
	 * @date 2018年6月28日
	 * @version V1.0
	 * @Title: hander
	 * @Description: 对从控制接口服务接收到的数据进行处理
	 * 			03 控制消息
	 * @param @param iotp    参数
	 * @return void    返回类型
	 * @throws
	 */
	private void hander(IOTPBean iotp) throws InterruptedException {
		switch (iotp.getType()) {
			// 控制消息
			case 3: 
				String address = getAddress(iotp);
				if(address != null) {
					write.send_single_address(address, iotp);
				}
				break;
			
			default:
				iotp = null; // 设置为null，加速GC回收
				break;
		}
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月28日
	 * @version V1.0
	 * @Title: getAddress
	 * @Description: TODO(这里用一句话描述这个方法的作用)
	 * @param @param iotp
	 * @param @return    参数
	 * @return String    返回类型
	 * @throws
	 */
	private String getAddress(IOTPBean iotp) {
		String config = iotp.getBody().getBodyList().get(0);
		CollectionAgentConfig cac = JSON.parseObject(config, CollectionAgentConfig.class);
		if(cac != null || cac.getIOServer_address() != null) {
			return cac.getIOServer_address();
		} else {
			return null;
		}
	}
}